package org.jeecg.modules.lottery.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.jeecg.modules.bid.entity.Bid;
import org.jeecg.modules.bid.service.IBidService;
import org.jeecg.modules.lottery.config.LotteryConfig;
import org.jeecg.modules.lottery.service.ILotteryRulesService;
import org.jeecg.modules.supplier.entity.Supplier;
import org.jeecg.modules.supplier.service.ISupplierService;
import org.jeecg.modules.supplierEvaluation.service.ISupplierEvaluationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

/**
 * 抽签规则服务实现类
 */
@Service
public class LotteryRulesServiceImpl implements ILotteryRulesService {

    @Autowired
    private LotteryConfig lotteryConfig;

    @Autowired
    private IBidService bidService;

    @Autowired
    private ISupplierService supplierService;

    @Autowired
    private ISupplierEvaluationService supplierEvaluationService;


       /**
     * 应用衰减因子
     */
    private void applyReductionFactors(List<Supplier> suppliers, String[] type ) {
        for (Supplier supplier : suppliers) {

            // 判断是否启用历史中标次数衰减规则
            double countReduction = 0;
            double paramsAmountThreshold = 0;
            if (type != null && Arrays.asList(type).contains("1")) {
                // 应用历史中标次数衰减因子，从proc_bid表查询供应商中标次数
                int winCount = getSupplierWinCount(supplier.getId());
                countReduction = calculateCountReduction(winCount, lotteryConfig.getParams());
                paramsAmountThreshold = lotteryConfig.getParamsAmountThreshold();
            }
            double amountReduction = 0;
            double paramsgetAmountReductionRate = 0;
            if (type != null && Arrays.asList(type).contains("2")) {
                // 2. 应用历史中标金额衰减因子，从proc_bid表查询供应商每条中标记录并单独计算衰减
                 amountReduction = calculateAmountReductionByRecords(supplier.getId(),
                        lotteryConfig.getThreshold(), lotteryConfig.getReductionRate());
                paramsgetAmountReductionRate =  lotteryConfig.getParamsgetAmountReductionRate();
            }

            double performanceReduction = 0;
            double performanceReductionMap = 0;
            if (type != null && Arrays.asList(type).contains("3")) {
                // 3. 应用履约评价衰减因子（从proc_supplier_evaluation表查询评分，根据评分衰减）
                // 从proc_supplier_evaluation表查询供应商平均评分
                BigDecimal avgScore = supplier.getAvgScore();
                // 将平均分转换为1-5的评价等级（假设评分范围是0-100）
                int performanceRating = 0;
                if (avgScore != null && avgScore.compareTo(BigDecimal.ZERO) > 0) {
                    // 将0-100分转换为1-5等级
                    performanceRating = Math.min(5, Math.max(1, (int) (avgScore.doubleValue() / 20) + 1));
                }

                // 由于calculatePerformanceReduction方法需要Map类型参数，而PerformanceReductionMap是double
                 performanceReduction = calculatePerformanceReduction(performanceRating, null);
                performanceReductionMap = lotteryConfig.getPerformanceReductionMap();
            }


            double comprehensiveFactor = calculateComprehensiveFactor(countReduction, amountReduction, performanceReduction,
                    paramsAmountThreshold, paramsgetAmountReductionRate, performanceReductionMap);
            supplier.setReduction(comprehensiveFactor);
            supplierService.updateSupplier(supplier);
            //保存衰减值
        }
    }

    /**
     * 计算次数衰减因子
     * 中标次数越多，衰减越大，呈线性关系
     * @param winCount 历史中标次数
     * @param reductionPerTime 每次中标衰减比例
     * @return 衰减因子（百分比）
     */
    private double calculateCountReduction(int winCount, double reductionPerTime) {
        // 确保不出现负数或过大的衰减值
        double reduction = winCount * reductionPerTime * 10;
        return Math.max(0, Math.min(lotteryConfig.getMaxReduction(), reduction)); // 最大衰减不超过配置值
    }

    /**
     * 根据每条投标记录计算金额衰减因子
     * 对每条中标记录单独计算衰减，然后累加
     * @param supplierId 供应商ID
     * @param threshold 金额阈值
     * @param reductionRate 超出部分的衰减率
     * @return 总衰减因子（百分比）
     */
    private double calculateAmountReductionByRecords(String supplierId, double threshold, double reductionRate) {
        if (supplierId == null || supplierId.trim().isEmpty() || threshold <= 0 || reductionRate <= 0) {
            return 0.0;
        }

        QueryWrapper<Bid> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("supplier_id", supplierId);
        queryWrapper.eq("is_winning", 1); // 1表示中标

        List<Bid> winningBids = bidService.list(queryWrapper);
        if (winningBids == null || winningBids.isEmpty()) {
            return 0.0;
        }

        double totalReduction = 0.0;
        for (Bid bid : winningBids) {
            if (bid.getBidPrice() != null) {
                double bidAmount = bid.getBidPrice().doubleValue();
                // 对每条记录单独计算衰减
                totalReduction += calculateSingleBidReduction(bidAmount, threshold, reductionRate);
            }
        }

        // 限制最大总衰减幅度
        return Math.min(lotteryConfig.getMaxReduction(), totalReduction); // 最大衰减不超过配置值
    }

    /**
     * 计算单条中标记录的衰减因子
     * 中标金额超过阈值后，超出部分按比例扣减
     * @param bidAmount 单条中标金额
     * @param threshold 金额阈值
     * @param reductionRate 超出部分的衰减率
     * @return 单条记录的衰减因子（百分比）
     */
    private double calculateSingleBidReduction(double bidAmount, double threshold, double reductionRate) {

        //如果超出threshold 金额，扣reductionRate
        if (bidAmount -threshold > 0.0) {
            return reductionRate * 10;
        }
        return 0.0;
    }

    /**
     * 计算金额衰减因子（保留原方法接口，供其他地方调用）
     * @param winAmount 历史中标金额
     * @param threshold 金额阈值
     * @param reductionRate 超出部分的衰减率
     * @return 衰减因子（百分比）
     */
    private double calculateAmountReduction(double winAmount, double threshold, double reductionRate) {
        // 直接调用单条记录的计算方法
        return calculateSingleBidReduction(winAmount, threshold, reductionRate);
    }

    /**
     * 计算履约评价衰减因子
     * 根据供应商履约评价等级计算衰减/奖励因子
     * @param rating 履约评价等级(1-5分)
     * @param reductionMap 自定义评价等级对应的衰减/奖励映射
     * @return 衰减/奖励因子（百分比，正值为扣减，负值为奖励）
     */
    private double calculatePerformanceReduction(int rating, Map<Integer, Double> reductionMap) {
        // 验证评分范围
        if (rating < 1 || rating > 5) {
            return 0.0;
        }

        // 使用自定义衰减映射或默认规则
        if (reductionMap != null && reductionMap.containsKey(rating)) {
            Double reduction = reductionMap.get(rating);
            return reduction != null ? reduction : 0.0;
        } else {
            // 默认衰减/奖励规则
            switch (rating) {
                case 1: return lotteryConfig.getPerformanceLevel1Reduction();  // 履约很差
                case 2: return lotteryConfig.getPerformanceLevel2Reduction();  // 履约较差
                case 3: return lotteryConfig.getPerformanceLevel3Reduction();  // 履约一般
                default: return 0.0;
            }
        }
    }

    /**
     * 从proc_bid表查询供应商中标次数
     * @param supplierId 供应商ID
     * @return 中标次数
     */
    public int getSupplierWinCount(String supplierId) {
        if (supplierId == null || supplierId.trim().isEmpty()) {
            return 0;
        }

        QueryWrapper<Bid> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("supplier_id", supplierId);
        queryWrapper.eq("is_winning", 1); // 1表示中标

        return Math.toIntExact(bidService.count(queryWrapper));

    }

    /**
     * 从proc_bid表查询供应商中标金额总和
     * @param supplierId 供应商ID
     * @return 中标金额
     */
    private double getSupplierTotalWinAmount(String supplierId) {
        if (supplierId == null || supplierId.trim().isEmpty()) {
            return 0.0;
        }

        QueryWrapper<Bid> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("supplier_id", supplierId);
        queryWrapper.eq("is_winning", 1); // 1表示中标

        List<Bid> winningBids = bidService.list(queryWrapper);
        if (winningBids == null || winningBids.isEmpty()) {
            return 0.0;
        }

        // 计算中标金额
        return winningBids.stream()
                .mapToDouble(bid -> bid.getBidPrice() != null ? bid.getBidPrice().doubleValue() : 0.0)
                .sum();
    }

    /**
     * 计算综合调整系数
     * 综合考虑历史中标次数、中标金额和履约评价的影响
     * @param countReduction 次数衰减因子
     * @param amountReduction 金额衰减因子
     * @param performanceReduction 履约评价衰减因子
     * @param countWeight 次数权重
     * @param amountWeight 金额权重
     * @param performanceWeight 评价权重
     * @return 综合调整系数
     */
    private double calculateComprehensiveFactor(double countReduction, double amountReduction,
                                              double performanceReduction, double countWeight, double amountWeight, double performanceWeight) {
        // 确保权重合理
        double validCountWeight = Math.max(0, Math.min(1, countWeight));
        double validAmountWeight = Math.max(0, Math.min(1, amountWeight));
        double validPerformanceWeight = Math.max(0, Math.min(1, performanceWeight));

        // 归一化权重，确保总和为1
        double totalWeight = validCountWeight + validAmountWeight + validPerformanceWeight;
        if (totalWeight <= 0) {
            // 如果所有权重都无效，则平均分配
            validCountWeight = 1.0 / 3;
            validAmountWeight = 1.0 / 3;
            validPerformanceWeight = 1.0 / 3;
        } else {
            validCountWeight /= totalWeight;
            validAmountWeight /= totalWeight;
            validPerformanceWeight /= totalWeight;
        }

        // 计算加权衰减值，包含履约评价的权重
        double factorReduction = (countReduction * validCountWeight) +
                               (amountReduction * validAmountWeight) +
                               (performanceReduction * validPerformanceWeight);

        // 计算综合调整系数，确保不会出现极值
        double factor = 1.0 - factorReduction / 100.0;

        // 限制调整系数范围：最小和最大值从配置获取
        return Math.max(lotteryConfig.getMinFactor(), Math.min(lotteryConfig.getMaxFactor(), factor));
    }

    /**
     * 计算调整后中签率
     */
    /**
     * 计算调整后中签率
     * 调整后中签率=基础中签率×综合调整系数
     */
    private void calculateAdjustedRates(List<Supplier> suppliers) {
        if (suppliers == null || suppliers.isEmpty()) {
            return;
        }
        // 计算基础中签率
        double baseRate = getBaseRate(suppliers.size());
        for (Supplier supplier : suppliers) {
            // 从供应商对象获取基础率和综合调整系数
            double comprehensiveFactor = supplier.getReduction() != null ? supplier.getReduction() : 1.0;

            // 调整后中签率=基础中签率×综合调整系数
            double adjustedRate = baseRate * comprehensiveFactor;
            // 确保调整后比例不会小于1%
            supplier.setWinningRate(adjustedRate);
            //保存中签率
            // 调用供应商服务更新数据库中的中签率
            supplierService.updateById(supplier);

        }

    }

    /**
     * 执行抽签
     * @param suppliers 供应商列表
     * @param drawCount 需要抽取的供应商数量
     * @return 中签的供应商列表
     */
    public List<Supplier> drawLottery(List<Supplier> suppliers, int drawCount) {
        List<Supplier> result = new ArrayList<>();

        // 参数校验
        if (suppliers == null || suppliers.isEmpty() || drawCount <= 0) {
            return result;
        }

        // 创建副本，避免修改原列表
        List<Supplier> remainingSuppliers = new ArrayList<>(suppliers);

        // 限制抽取数量不超过供应商总数
        int actualDrawCount = Math.min(drawCount, remainingSuppliers.size());

        // 随机数生成器
        Random random = new Random();
        // 应用衰减因子（历史中标次数、中标金额和履约评价）
        applyReductionFactors(suppliers, lotteryConfig.getType());

        // 循环抽取指定数量的供应商
        for (int i = 0; i < actualDrawCount; i++) {
            // 1、调用最终中签率方法计算各供应商的概率
            // 计算调整后中签率（基础中签率 × 综合调整系数）
            calculateAdjustedRates(remainingSuppliers);

            // 2、归一化处理，确保概率总和为100%
            normalizeRates(remainingSuppliers);

            // 3、根据概率进行抽取
            Supplier selectedSupplier = selectSupplierByProbability(remainingSuppliers, random);

            // 4、将选中的供应商添加到结果列表
            if (selectedSupplier != null) {
                result.add(selectedSupplier);
                // 从剩余供应商中移除已选中的供应商
                remainingSuppliers.remove(selectedSupplier);
            } else {
                // 如果未选中供应商（理论上不应该发生），则跳出循环
                break;
            }
        }

        return result;
    }

    /**
     * 根据概率权重随机选择一个供应商
     * @param suppliers 供应商列表
     * @param random 随机数生成器
     * @return 选中的供应商
     */
    private Supplier selectSupplierByProbability(List<Supplier> suppliers, Random random) {
        if (suppliers == null || suppliers.isEmpty()) {
            return null;
        }

        // 生成0-100之间的随机数
        double randomValue = random.nextDouble() * 100.0;

        // 累积概率，用于确定随机数落在哪个供应商的概率区间
        double cumulativeProbability = 0.0;

        for (Supplier supplier : suppliers) {
            Double winningRate = supplier.getWinningRate();
            if (winningRate != null) {
                cumulativeProbability += winningRate;
                // 如果随机值小于等于累积概率，则选中当前供应商
                if (randomValue <= cumulativeProbability) {
                    return supplier;
                }
            }
        }

        // 如果由于精度问题没有选中任何供应商，返回第一个供应商
        return suppliers.isEmpty() ? null : suppliers.get(0);
    }

    /**
     * 归一化处理
     * @param suppliers 供应商列表
     * @return 归一化后的供应商列表
     */
    public List<Supplier> normalizeRates(List<Supplier> suppliers) {
        if (suppliers == null || suppliers.isEmpty()) {
            return Collections.emptyList();
        }

        // 计算调整后概率总和
        double totalAdjustedRate = 0.0;
        for (Supplier supplier : suppliers) {
            Double adjustedRate = supplier.getWinningRate();
            if (adjustedRate != null) {
                totalAdjustedRate += adjustedRate;
            }
        }

        // 归一化处理
        double totalFinalRate = 0.0;
        for (Supplier supplier : suppliers) {
            Double adjustedRate = supplier.getWinningRate();
            if (adjustedRate != null && totalAdjustedRate > 0) {
                double finalRate = (adjustedRate / totalAdjustedRate) * 100.0;
                //转换为一个总和为100
                supplier.setWinningRate(finalRate);
                totalFinalRate += finalRate;
            } else {
                supplier.setWinningRate(0.0);
            }
        }

        // 处理精度问题，确保总和为100%
        adjustFinalRatesForPrecision(suppliers, totalFinalRate);

        return suppliers;
    }

    /**
     * 调整最终概率以处理精度问题
     */
    private void adjustFinalRatesForPrecision(List<Supplier> suppliers, double totalFinalRate) {
        // 检查suppliers是否为null或空
        if (suppliers == null || suppliers.isEmpty()) {
            return;
        }

        double difference = 100.0 - totalFinalRate;
        if (Math.abs(difference) > 0.0001) { // 只有当差异大于0.0001时才调整
            // 找到概率最大的供应商进行调整，使用更安全的方式处理null值
            Supplier maxRateSupplier = suppliers.stream()
                    .filter(s -> s != null)
                    .max(Comparator.comparing((Supplier s) -> {
                        Double rate = s.getWinningRate();
                        return rate != null ? rate : 0.0;
                    }))
                    .orElseGet(() -> suppliers.stream()
                            .filter(s -> s != null)
                            .findFirst()
                            .orElse(null));

            // 如果找不到有效供应商，则不进行调整
            if (maxRateSupplier != null) {
                Double currentRate = maxRateSupplier.getWinningRate();
                if (currentRate != null) {
                    // 确保调整后概率不为负数
                    maxRateSupplier.setWinningRate(Math.max(0.0, currentRate + difference));
                }
            }
        }
    }

    /**
     * 获取基础中签率
     * @param supplierCount 供应商数量
     * @return 基础中签率
     */

    public double getBaseRate(int supplierCount) {
        // 简化版：均等分配基础中签率
        if (supplierCount <= 0) {
            return 0.0;
        }
        return 100.0 / supplierCount;
    }
}
